import socket

import ter
import time
import threading
import struct
class TBus104Ly( ter.TIotBus):
    def __init__(self , ter ):
        super().__init__(ter)
        self.client = None
        self.started = False
        self.running = False
        self.connected = False
        self.varsByAddr = {}

        self.sendThreadStarted = False

        self.host = ""
        self.port = 2404
        #报文序号
        self.noRec = 0
        self.noSend = 0

        #times
        self.totalTime = 0
        self.totalTimeThreshold = 5 * 60 * 1

    def init (self):
        try:
            super().init()
            for varValue in self.values.values():
                addr = varValue.var['regAddr']
                self.varsByAddr[addr] = varValue
        except Exception as er:
            print(er)
    def isConnected (self):
        res = False
        try:
            if self.client and self.connected  :
                res = True
        except Exception as er:
            print(er)
        return res
    def start (self):
        try:
            self.stop()
            self.started = True
            try:
                print('ice104 client start ...')
                while self.started:
                    time.sleep(1)
                    try:
                        self.exec()
                    except Exception as erRun:
                        print(erRun)
                        break
            except Exception as eer:
                print(eer)
            finally:
                self.stop()
        except Exception as er:
            print(er)
    def stop (self):
        try:
            self.started = False
            self.running = False
            if self.client!=None:
                try:
                    self.client.shutdown(socket.SHUT_RDWR)
                    self.close()
                except Exception as erShutdown:
                    print(erShutdown)
                self.client = None
            self.connected = False
        except Exception as er:
            print(er)
    def exec  (self):
        try:
            print('ice104 client execute ...')
            self.running = True
            self.initSession()
            self.connectHost()
            print(f'connected:{self.connected} . running:{self.running} .started:{self.started}')
            while self.connected and self.running and self.started :
                try:
                    status , msg = self.recMsg()
                    if status==1 :
                        self.doOnRecMsg(msg)
                    else:
                        print('rec msg error.break')
                        break
                except Exception as erRec:
                    print(erRec)
            self.running = False
            self.connected = False
            if self.client!=None:
                try:
                    self.client.shutdown(socket.SHUT_RDWR)
                except Exception as err:
                    print(err)
                try:
                    self.client.close()
                except Exception as err:
                    print(err)
                self.client = None
            print('execute end.')
        except Exception as er:
            print(er)
    def initSession (self):
        try:
            busSettings = self.ter.terData['buses'][self.busCode]
            self.host = busSettings['host']
            self.port = busSettings['port']
            self.noRec = 0
            self.noSend = 0
            self.connected = False
            print(f'init ice104 client.  {self.host}:{self.port}')
        except Exception as er:
            print(er)
    def connectHost (self):
        try:
            self.client = socket.socket()
            connFlag = self.client.connect_ex((self.host , self.port))
            print(f'ice 104 connect:{connFlag}')
            if connFlag == 0:
                self.connected = True
            else:
                self.connected = False
                print(f'connect {self.host}:{self.port} error!')
        except Exception as er:
            print(er)
    def recMsg (self):
        msg = None
        status = 0
        try:
            print('ice 104 client recieve msg...')
            while self.connected :
                recStatus , rec = self.readByte()
                if recStatus==1 :
                    if msg==None and rec[0]==0x68:
                        msg = [rec[0]]
                    elif msg!=None:
                        msg.append(rec[0])
                        endFlag = (msg[1] +2) == len(msg)
                        if endFlag :
                            status = 1
                            break
                else:
                    break
            #print(f'ice 104 client recieve msg end.status:{status}.msg:{msg}')
        except Exception as er:
            print(er)
        return  status, msg
    def readByte (self , retry = 10 ):
        res = None
        status = 0
        try:
            count = 0
            while count<retry:
                try:
                    res = self.client.recv(1)
                    if len(res)>0:
                        status = 1
                        break
                    else:
                        count = count +1
                except Exception as erRead:
                    print(erRead)
                    break
        except Exception as er:
            print(er)
        return  status, res
    def doOnRecMsg (self , msg):
        try:
            if len(msg)==6 and (msg[2] & 0x01 ) ==1  and (msg[2] | 0x02 ) ==0 and (msg[4] | 0x01 ) ==0 :
                print('s frame')
            elif (len(msg)==6) and (msg[2]==0x43):
                respCmds = bytearray([0x68, 0x04, 0x83, 0, 0, 0])
                self.client.sendall(respCmds)
                print(f'response msg:{self.getBytesStr(respCmds)}')
            elif ((msg[2] & 0x01 ) ==0)   and  ((msg[4] & 0x01 ) ==0):
                print('I frame')
                noRec = int.from_bytes( msg[4:6] , byteorder='little')
                noRec = noRec + 2
                if noRec>=0xffff:
                    noRec = 0
                self.noRec = noRec
                bytes = int.to_bytes(noRec , 2 , byteorder='little')
                cmds = bytearray([0x68,0x04,0x01,0,bytes[0],bytes[1]])
                self.client.sendall(cmds)
                decStatus , decMsg = self.decoder(msg)
                if decStatus ==  1 :
                    reason = decMsg['reason']
                    if reason==3 or reason==20:
                        print('decode success')
                        print(decMsg)
                        self.upload(decMsg)
                else:
                    print(f'decode msg error.{self.getBytesStr(msg)}')
            else:
                print(f'rec other msg:{self.getBytesStr(msg)}')

            if not self.sendThreadStarted:
                self.sendThreadStarted = True
                sendThread = threading.Thread( target=self.sendThreadExecute , daemon=True)
                sendThread.start()

        except Exception as er:
            print(er)
    def sendThreadExecute (self ):
        try:
            self.sendThreadStarted = True
            cmd_start = bytearray([0x68,0x04,0x07,0,0,0 ])
            self.client.sendall(cmd_start)
            print(f'start:{cmd_start}')
            while self.connected and self.running and self.started :
                time.sleep(1)
                self.sendFrame_total()
        except Exception as er:
            print(er)
        finally:
            self.sendThreadStarted = False
    def getBytesStr (self , bytes):
        res =[]
        try:
            def encodeByte( bt):
                s = hex(bt)
                if len(s)==3:
                    s = s[0]+s[1]+'0'+s[2]
                return  s
            res = [ encodeByte(x) for x in bytes]
        except Exception as er:
            print(er)
        return res
    def sendFrame_total (self):
        try:
            timeout  =( time.time() - self.totalTime)> self.totalTimeThreshold
            if timeout:
                print('total call.')
                self.totalTime = time.time()
                sendNoBytes = int.to_bytes(self.noSend ,2 , byteorder='little')
                recNoBytes = int.to_bytes(self.noSend ,2 , byteorder='little')
                cmds = bytearray([0x68, 0x0e , sendNoBytes[0] , sendNoBytes[1] , recNoBytes[0] , recNoBytes[0] , 0x64 , 0x01 , 0x06 , 0x00 , 0x01, 0x00 , 0x00 , 0x00 ,0x00 , 0x14])
                self.client.sendall(cmds)
                print(f'total call cmd{self.getBytesStr(cmds)}.')

        except Exception as er:
            print(er)


    def decoder(self , msg):
        res ={
            "pgkSize":msg[1] ,
            "noSL":msg[2] ,
            "noSH":msg[3] ,
            "noRL":msg[4] ,
            "noRH":msg[5] ,

            "type": msg[6],
            "addrContinueFlag":0 if msg[7] & 0x80==0 else 1 ,
            "varNums":msg[7] & 0x7f,
            "reason": msg[8],
            "unitId": 256 * msg[11] + msg[10],
            "vars":[]
        }
        print(res)
        status = 0
        try:
            varNum = res['varNums']
            typ = res['type']
            addrContinueFlag = res['addrContinueFlag']
            varSize = 0
            startAddr = 0
            varDataPos = 0
            if varNum>0 :
                if typ == 1 and addrContinueFlag==0:
                    #遥信开关量 , 地址不连续
                    varSize = 3 + 1
                    varDataPos = 12
                elif typ == 1 and addrContinueFlag==1:
                    #遥信开关量 , 地址连续
                    varSize = 1
                    varDataPos = 12+3
                    startAddr =  256*msg[13]+msg[12]
                elif typ == 0x0d and addrContinueFlag==0:
                    #遥测短浮点数， 4字节 , 地址不连续
                    varSize = 3 + 4 +  1
                    varDataPos = 12
                elif typ == 0x0d and addrContinueFlag==1:
                    #遥测短浮点数， 4字节 , 地址连续
                    varSize = 4 +  1
                    varDataPos = 12+3
                    startAddr =  256*msg[13]+msg[12]


                for i in range(varNum):
                    varFlag = 0
                    varAddr = 0
                    varValue = 0
                    varFrom = varDataPos + i * varSize
                    varEnd = varFrom + varSize
                    varBytes = msg[varFrom: varEnd]
                    varDataBytes =None

                    # var address
                    if addrContinueFlag== 0 :
                        varAddr =  256*varBytes[1]+ varBytes[0]
                        varDataBytes = varBytes[3:]
                    elif addrContinueFlag== 1 :
                        varAddr =  startAddr + i
                        varDataBytes = varBytes
                    # var value
                    if typ == 0x01 :
                        varValue = varDataBytes[0]
                        varFlag = 1
                    elif typ == 0x0d:
                        #varValue = int.from_bytes(bytearray(varDataBytes)  , byteorder='little')
                        #print(varDataBytes)
                        try:
                            varValue = (struct.unpack("<f" , bytearray(varDataBytes[:4])))[0]
                            varFlag = 1
                        except Exception as erFloat:
                            print(erFloat)

                    if varFlag == 1 :
                        a = varValue
                        if  len(str(a)) - len(str(int(a))) >3:
                            a = float(str(a)[0:len(str(int(a))) + 3])
                            varValue = a
                        varInfo = {
                            "order": i ,
                            "varAddr" : varAddr ,
                            "varValue": varValue
                        }
                        res['vars'].append(varInfo)
            status = 1
        except Exception as er :
            print(er)
        return  status , res
    def upload(self , msg):
        try:
            varValues =[]
            nowTm = time.time()
            for item in msg['vars']:
                reason = msg['reason']
                typ = msg['type']
                flag = (reason == 20) or ( reason==13 or typ == 1 )
                if flag :
                    addr = item['varAddr']
                    if addr in self.varsByAddr.keys():
                        varValue = self.varsByAddr[addr]
                        varValue.tm0 = varValue.tm1
                        varValue.v0 = varValue.v1
                        varValue.tm1 = nowTm
                        value = item['varValue']
                        varValue.v1 = value
                        varValue.disp = str(value)
                        varValue.changed = True
                        varValues.append(varValue)

                        print(f' set var value: {item} ---> {varValue}')


                '''
                varObj = getVarValueObj(item['varAddr'])
                if varObj!=None:
                    uploadFlag = getUploadFlag( item)
                    if uploadFlag :
                        setVarValue( item , varObj)
                        varValues.append(varObj)
                '''
            if len(varValues)>0:
                self.uploadVarvalues(None , varValues)
                self.updateServerVar(None , varValues )
                self.updateRegisRealValue(varValues)
                for item in varValues:
                    item.v0 = item.v1
                    item.tm0 = item.tm1
                    item.changed = False
        except Exception as er :
            print(er)



class T104Var:
    def __init__(self , msg) :
        self.msg = msg
    def decoder(self):
        res ={
            "pgkSize":self.msg[1] ,
            "noSL":self.msg[2] ,
            "noSH":self.msg[3] ,
            "noRL":self.msg[4] ,
            "noRH":self.msg[5] ,

            "type": self.msg[6],
            "addrContinueFlag":0 if self.msg[7] & 0x80==0 else 1 ,
            "varNums":self.msg[7] & 0x7f,
            "reason": self.msg[8],
            "unitId": 256 * self.msg[11] + self.msg[10],
            "vars":[]
        }
        status = 0
        try:
            varNum = res['varNums']
            typ = res['type']
            addrContinueFlag = res['addrContinueFlag']
            varSize = 0
            startAddr = 0
            varDataPos = 0
            if varNum>0 :
                if typ == 1 and addrContinueFlag==0:
                    #遥信开关量 , 地址不连续
                    varSize = 3 + 1
                    varDataPos = 12
                elif typ == 1 and addrContinueFlag==1:
                    #遥信开关量 , 地址连续
                    varSize = 1
                    varDataPos = 12+3
                    startAddr =  256*self.msg[13]+self.msg[12]
                elif typ == 0x0d and addrContinueFlag==0:
                    #遥测短浮点数， 4字节 , 地址不连续
                    varSize = 3 + 4 +  1
                    varDataPos = 12
                elif typ == 0x0d and addrContinueFlag==1:
                    #遥测短浮点数， 4字节 , 地址连续
                    varSize = 4 +  1
                    varDataPos = 12+3
                    startAddr =  256*self.msg[13]+self.msg[12]

                for i in range(varNum):
                    varFlag = 0
                    varAddr = 0
                    varValue = 0
                    varFrom = varDataPos + i * varSize
                    varEnd = varFrom + varSize
                    varBytes = self.msg[varFrom , varEnd]
                    varDataBytes =None

                    # var address
                    if addrContinueFlag== 0 :
                        varAddr =  256*varBytes[1]+ varBytes[0]
                        varDataBytes = varBytes[3:]
                    elif addrContinueFlag== 1 :
                        varAddr =  startAddr + i
                        varDataBytes = varBytes
                    # var value
                    if typ == 0x01 :
                        varValue = varDataBytes[0]
                        varFlag = 1
                    elif typ == 0x0d:
                        varValue = int.from_bytes(varDataBytes , 4 , byteorder='little')
                        varFlag = 1

                    if varFlag == 1 :
                        varInfo = {
                            "order": i ,
                            "varAddr" : varAddr ,
                            "varValue": varValue
                        }
                        res['vars'].append(varInfo)
            status = 1
        except Exception as er :
            print(er)
        return  status , res
ter.iotTer.registBusType('104ly' , TBus104Ly)